package com.info.zhiduoduo.common.utils;

import net.coobird.thumbnailator.Thumbnails;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.math.BigInteger;
import java.net.FileNameMap;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

public class FileUtil {

	/**
	 * 拼接路径
	 * concatPath("/mnt/sdcard", "/DCIM/Camera")  	=>		/mnt/sdcard/DCIM/Camera
	 * concatPath("/mnt/sdcard", "DCIM/Camera")  	=>		/mnt/sdcard/DCIM/Camera
	 * concatPath("/mnt/sdcard/", "/DCIM/Camera")  =>		/mnt/sdcard/DCIM/Camera
	 */
	public static String concatPath(String... paths) {
		StringBuilder result = new StringBuilder();
		if (paths != null) {
			for (String path : paths) {
				if (path != null && path.length() > 0) {
					int len = result.length();
					boolean suffixSeparator = len > 0 && result.charAt(len - 1) == File.separatorChar;//后缀是否是'/'
					boolean prefixSeparator = path.charAt(0) == File.separatorChar;//前缀是否是'/'
					if (suffixSeparator && prefixSeparator) {
						result.append(path.substring(1));
					} else if (!suffixSeparator && !prefixSeparator) {//补前缀
						result.append(File.separatorChar);
						result.append(path);
					} else {
						result.append(path);
					}
				}
			}
		}
		return result.toString();
	}

	/**
	 * 计算文件的md5值
	 */
	public static String calculateMD5(File updateFile) {
		MessageDigest digest;
		try {
			digest = MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException e) {
			LogUtil.error("Exception while getting digest");
			return null;
		}

		InputStream is;
		try {
			is = new FileInputStream(updateFile);
		} catch (FileNotFoundException e) {
			return null;
		}

		//DigestInputStream

		byte[] buffer = new byte[8192];
		int read;
		try {
			while ((read = is.read(buffer)) > 0) {
				digest.update(buffer, 0, read);
			}
			byte[] md5sum = digest.digest();
			BigInteger bigInt = new BigInteger(1, md5sum);
			String output = bigInt.toString(16);
			// Fill to 32 chars
			output = String.format("%32s", output).replace(' ', '0');
			return output;
		} catch (IOException e) {
			throw new RuntimeException("Unable to process file for MD5", e);
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				LogUtil.error("Exception on closing MD5 input stream");
			}
		}
	}

	/**
	 * 计算文件的md5值
	 */
	public static String calculateMD5(File updateFile, int offset, int partSize) {
		MessageDigest digest;
		try {
			digest = MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException e) {
			LogUtil.error("Exception while getting digest");
			return null;
		}

		InputStream is;
		try {
			is = new FileInputStream(updateFile);
		} catch (FileNotFoundException e) {
			LogUtil.error("Exception while getting FileInputStream");
			return null;
		}

		//DigestInputStream
		final int buffSize = 8192;//单块大小
		byte[] buffer = new byte[buffSize];
		int read;
		try {
			if (offset > 0) {
				is.skip(offset);
			}
			int byteCount = Math.min(buffSize, partSize), byteLen = 0;
			while ((read = is.read(buffer, 0, byteCount)) > 0 && byteLen < partSize) {
				digest.update(buffer, 0, read);
				byteLen += read;
				//检测最后一块，避免多读数据
				if (byteLen + buffSize > partSize) {
					byteCount = partSize - byteLen;
				}
			}
			byte[] md5sum = digest.digest();
			BigInteger bigInt = new BigInteger(1, md5sum);
			String output = bigInt.toString(16);
			// Fill to 32 chars
			output = String.format("%32s", output).replace(' ', '0');
			return output;
		} catch (IOException e) {
			throw new RuntimeException("Unable to process file for MD5", e);
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				LogUtil.error("Exception on closing MD5 input stream");
			}
		}
	}

	/**
	 * 检测文件是否可用
	 */
	public static boolean checkFile(File f) {
		if (f != null && f.exists() && f.canRead() && (f.isDirectory() || (f.isFile() && f.length() > 0))) {
			return true;
		}
		return false;
	}

	/**
	 * 检测文件是否可用
	 */
	public static boolean checkFile(String path) {
		if (!StringUtil.isEmpty(path)) {
			File f = new File(path);
			if (f != null && f.exists() && f.canRead() && (f.isDirectory() || (f.isFile() && f.length() > 0))) return true;
		}
		return false;
	}

	/**
	 * 计算文件或者文件夹的大小 ，单位 KB
	 *
	 * @param file 要计算的文件或者文件夹 ， 类型：java.io.File
	 * @return 大小，单位：KB
	 */
	public static double getFileSize(File file) {
		//判断文件是否存在
		if (file == null) {
			return 0.0;
		}
		if (file.exists()) {
			//如果是目录则递归计算其内容的总大小，如果是文件则直接返回其大小
			if (!file.isFile()) {
				//获取文件大小
				File[] fl = file.listFiles();
				double ss = 0;
				if (fl != null) {
					for (File f : fl) {ss += getFileSize(f);}
				}
				return ss;
			} else {
				double ss = (double) file.length() / 1024;
				System.out.println(file.getName() + " : " + ss + "KB");
				return ss;
			}
		} else {
			System.out.println("文件或者文件夹不存在，请检查路径是否正确！");
			return 0.0;
		}
	}

	public static long getFileSize(String fn) {
		File f = null;
		long size = 0;

		try {
			f = new File(fn);
			size = f.length();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			f = null;
		}
		return size < 0 ? null : size;
	}

	public static String getFileType(String fn, String defaultType) {
		FileNameMap fNameMap = URLConnection.getFileNameMap();
		String type = fNameMap.getContentTypeFor(fn);
		return type == null ? defaultType : type;
	}

	public static String getFileType(String fn) {
		return getFileType(fn, "application/octet-stream");
	}

	public static String getFileExtension(String filename) {
		String extension = "";
		if (filename != null) {
			int dotPos = filename.lastIndexOf(".");
			if (dotPos >= 0 && dotPos < filename.length() - 1) {
				extension = filename.substring(dotPos + 1);
			}
		}
		return extension.toLowerCase();
	}

	public static boolean deleteFile(File f) {
		if (f != null && f.exists() && !f.isDirectory()) {
			return f.delete();
		}
		return false;
	}

	public static void deleteDir(File f) {
		if (f != null && f.exists() && f.isDirectory()) {
			for (File file : f.listFiles()) {
				if (file.isDirectory()) deleteDir(file);
				file.delete();
			}
			f.delete();
		}
	}

	public static void deleteCacheFile(String f) {
		if (f != null && f.length() > 0) {
			File files = new File(f);
			if (files.exists() && files.isDirectory()) {
				for (File file : files.listFiles()) {
					if (!file.isDirectory() && (file.getName().contains(".ts") || file.getName().contains("temp"))) {
						file.delete();
					}

				}
			}
		}
	}

	public static void deleteCacheFile2TS(String f) {
		if (f != null && f.length() > 0) {
			File files = new File(f);
			if (files.exists() && files.isDirectory()) {
				for (File file : files.listFiles()) {
					if (!file.isDirectory() && (file.getName().contains(".ts"))) {
						file.delete();
					}

				}
			}
		}
	}

	public static void deleteDir(String f) {
		if (f != null && f.length() > 0) {
			deleteDir(new File(f));
		}
	}

	public static boolean deleteFile(String f) {
		if (f != null && f.length() > 0) {
			return deleteFile(new File(f));
		}
		return false;
	}

	/**
	 * read file
	 *
	 * @param charsetName The name of a supported {@link java.nio.charset.Charset
	 *                    </code>charset<code>}
	 * @return if file not exist, return null, else return content of file
	 * @throws RuntimeException if an error occurs while operator BufferedReader
	 */
	public static String readFile(File file, String charsetName) {
		StringBuilder fileContent = new StringBuilder("");
		if (file == null || !file.isFile()) {
			return fileContent.toString();
		}

		BufferedReader reader = null;
		try {
			InputStreamReader is = new InputStreamReader(new FileInputStream(file), charsetName);
			reader = new BufferedReader(is);
			String line = null;
			while ((line = reader.readLine()) != null) {
				if (!fileContent.toString().equals("")) {
					fileContent.append("\r\n");
				}
				fileContent.append(line);
			}
			reader.close();
		} catch (IOException e) {
			throw new RuntimeException("IOException occurred. ", e);
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
					throw new RuntimeException("IOException occurred. ", e);
				}
			}
		}
		return fileContent.toString();
	}

	public static String readFile(String filePath, String charsetName) {
		return readFile(new File(filePath), charsetName);
	}

	public static String readFile(File file) {
		return readFile(file, "utf-8");
	}

	/**
	 * 文件拷贝
	 *
	 * @param from
	 * @param to
	 * @return
	 */
	public static boolean fileCopy(String from, String to) {
		boolean result = false;

		int size = 1 * 1024;

		FileInputStream in = null;
		FileOutputStream out = null;
		try {
			in = new FileInputStream(from);
			out = new FileOutputStream(to);
			byte[] buffer = new byte[size];
			int bytesRead = -1;
			while ((bytesRead = in.read(buffer)) != -1) {
				out.write(buffer, 0, bytesRead);
			}
			out.flush();
			result = true;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (in != null) {
					in.close();
				}
			} catch (IOException e) {
			}
			try {
				if (out != null) {
					out.close();
				}
			} catch (IOException e) {
			}
		}
		return result;
	}

	/**
	 * 获取图片类型
	 *
	 * @param url
	 * @return
	 */
	private static String pictureType(String url) {
		if (url.contains(".jpeg")) {
			return ".jpg";
		}
		if (url.contains(".png")) {
			return ".png";
		}
		if (url.contains(".gif")) {
			return ".gif";
		}
		if (url.contains(".tif")) {
			return ".tif";
		}
		if (url.contains(".psd")) {
			return ".psd";
		}
		return null;
	}

	/**
	 * 下载文件
	 *
	 * @param urlString 原文件路径
	 * @param filePath  上传路径
	 * @param type      文件类型 1.视频 2.图片 3.压缩包
	 * @throws Exception
	 */
	public static String downloadFile(String urlString, String filePath, int type, String folder) throws Exception {
		String format = "yyyyMMdd";
		//文件夹名称
		String folderName = folder + "/" + DateUtil.DateToStr(format, new Date());
		//创建文件夹
		File folderFile = new File(filePath + folderName);
		if (!folderFile.exists()) {
			if (!folderFile.mkdirs()) {
				LogUtil.error("新建文件存放文件夹失败！");
				return null;
			}
		}
		String fileName = "";
		if (1 == type) {
			fileName = UUID.randomUUID() + "-" + System.currentTimeMillis() + ".mp4";
		} else if (2 == type) {
			fileName = UUID.randomUUID() + "-" + System.currentTimeMillis() + ".jpg";
		} else if (3 == type) {
			fileName = UUID.randomUUID() + "-" + System.currentTimeMillis() + ".zip";
		}
		//构造URL
		URL url = new URL(urlString);
		//打开连接
		URLConnection con = url.openConnection();
		//设置请求超时为5s
		con.setConnectTimeout(5 * 1000);
		//输入流
		InputStream is = con.getInputStream();
		//1K的数据缓冲
		byte[] bs = new byte[1024];
		//读取到的数据长度
		int len;
		OutputStream os = new FileOutputStream(folderFile.getPath() + File.separator + fileName);
		//开始读取
		while ((len = is.read(bs)) != -1) {
			os.write(bs, 0, len);
		}
		//完毕，关闭所有链接
		os.flush();
		os.close();
		is.close();
		return "/" + folderName + "/" + fileName;
	}

	/**
	 * 上传文件
	 *
	 * @param filePath
	 * @param imgFile
	 * @return
	 */
	public static String uploadFile(String filePath, MultipartFile imgFile) {
		String format = "yyyyMMdd";
		//文件夹名称
		String folderName = "confirm/" + DateUtil.DateToStr(format, new Date());
		//创建文件夹
		File folderFile = new File(filePath + folderName);
		if (!folderFile.exists()) {
			if (!folderFile.mkdirs()) {
				return "";
			}
		}
		//获取文件名
		String fileName = imgFile.getOriginalFilename();
		if (StringUtils.isEmpty(fileName)) {
			return "";
		}
		//获取文件后缀名
		String suffixName = fileName.substring(fileName.lastIndexOf("."));
		//新文件名
		String newFileName = UUID.randomUUID() + suffixName;
		//文件存放路径
		String uploadPath = filePath + "/" + folderName + "/" + newFileName;
		File uploadFile = new File(uploadPath);
		if (!uploadFile.getParentFile().exists()) {
			if (!uploadFile.getParentFile().mkdir()) {
				return "";
			}
		}
		BufferedOutputStream out = null;
		try {
			out = new BufferedOutputStream(new FileOutputStream(uploadFile));
			System.out.println("==============multFile.getBytes()==================");
			out.write(imgFile.getBytes());
			System.out.println("==============multFile.getBytes()==================");
			out.flush();
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return "/" + folderName + "/" + newFileName;
	}

	public static String uploadFileOwnName(String filePath, MultipartFile imgFile) {
		String format = "yyyyMMdd";
		//文件夹名称
		String folderName = "confirm/" + DateUtil.DateToStr(format, new Date());
		//创建文件夹
		File folderFile = new File(filePath + folderName);
		if (!folderFile.exists()) {
			if (!folderFile.mkdirs()) {
				return "";
			}
		}
		//获取文件名
		String fileName = imgFile.getOriginalFilename();
		if (StringUtils.isEmpty(fileName)) {
			return "";
		}
		//获取文件前缀名
		String prefixName = fileName.substring(0, fileName.lastIndexOf("."));
		//获取文件后缀名
		String suffixName = fileName.substring(fileName.lastIndexOf("."));
		String end = DateUtil.DateToStr("yyyy-MM-dd HH:mm:ss", new Date()).replaceAll(" ", "-").replaceAll(":", "-");
		//新文件名
		String newFileName = prefixName + end + suffixName;
		//文件存放路径
		String uploadPath = filePath + "/" + folderName + "/" + newFileName;
		File uploadFile = new File(uploadPath);
		if (!uploadFile.getParentFile().exists()) {
			if (!uploadFile.getParentFile().mkdir()) {
				return "";
			}
		}
		BufferedOutputStream out = null;
		try {
			out = new BufferedOutputStream(new FileOutputStream(uploadFile));
			System.out.println("==============multFile.getBytes()==================");
			out.write(imgFile.getBytes());
			System.out.println("==============multFile.getBytes()==================");
			out.flush();
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return "/" + folderName + "/" + newFileName;
	}

	/**
	 * File转Base64
	 *
	 * @param file
	 * @return
	 */
	public static String fileToBase64(File file) {
		if (file == null) {
			return null;
		}
		String base64 = null;
		FileInputStream fin = null;
		try {
			fin = new FileInputStream(file);
			byte[] buff = new byte[fin.available()];
			fin.read(buff);
			base64 = Base64.encode(buff);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fin != null) {
				try {
					fin.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return base64;
	}

	public static byte[] fileToBytes(File file) {
		try {
			FileInputStream stream = new FileInputStream(file);
			ByteArrayOutputStream output = new ByteArrayOutputStream();
			byte[] buffer = new byte[4096];
			int n = 0;
			while (-1 != (n = stream.read(buffer))) {
				output.write(buffer, 0, n);
			}
			return output.toByteArray();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 图片压缩
	 *
	 * @param filePath   原文件路径
	 * @param uploadPath 上传路径
	 * @return
	 */
	public static String compressPicture(String filePath, String uploadPath) {
		int i = filePath.lastIndexOf("/");
		String fileName = filePath.substring(i);
		String path = uploadPath + "/compress" + fileName;
		try {
			//            Thumbnails.of("/usr/local/oss/zdd-img" + filePath).scale(1f).outputQuality(0.15f).outputFormat("jpg").toFile
			//            (path);
			Thumbnails.of("/usr/local/oss/zdd-img" + filePath).size(640, 854).outputQuality(0.25f).outputFormat("jpg").toFile(path);
		} catch (Exception e) {
			e.printStackTrace();
			return "压缩失败！";
		}
		System.out.println("压缩图片后的路径为:" + path);
		return path;
	}

	/**
	 * 压缩视频
	 *
	 * @param convertFile 原文件路径
	 * @param uploadPath  上传路径
	 */
	public static String compressVideo(String convertFile, String uploadPath) {
		LogUtil.info("进入视频压缩！");

		//获取文件后缀名
		String suffixName = convertFile.substring(convertFile.lastIndexOf("."));
		//随机名字+后缀
		String fileName = UUID.randomUUID() + suffixName;
		String path = uploadPath + "videoCompress" + "/" + fileName;
		try {
			Runtime runtime = Runtime.getRuntime();
			String shell = "sh /usr/local/zhiduoduotest/merge.sh " + "/usr/local/oss/zdd-img" + convertFile + " " + path;
			Process process = runtime.exec(shell);
			LogUtil.info("---视频压缩中---");
			process.waitFor();
			LogUtil.info("---视频压缩结束---");
			return path;
		} catch (Exception e) {
			e.printStackTrace();
			LogUtil.info("压缩文件出现异常：" + e.getMessage());
			return "压缩失败！";
		}
	}

	/**
	 * 将文件夹压缩为压缩包
	 *
	 * @param srcSource
	 * @return
	 * @throws Exception
	 */
	public static byte[] createZip(String srcSource) throws Exception {
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		ZipOutputStream zip = new ZipOutputStream(outputStream);
		// 将目标文件打包成zip导出
		File file = new File(srcSource);
		compress(zip, file, "");
		IOUtils.closeQuietly(zip);
		return outputStream.toByteArray();
	}

	/**
	 * 将文件夹压缩为压缩包并写入response
	 *
	 * @param response
	 * @param folder 文件夹路径
	 * @param zipName    压缩包名称
	 * @return
	 * @throws Exception
	 */
	public static void createZip2Response(HttpServletResponse response, String folder, String zipName) throws Exception {
		// 调用方法打包zip文件
		byte[] zip = FileUtil.createZip(folder);
		response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(zipName, "utf-8"));
		response.addHeader("Content-Length", "" + zip.length);
		response.setContentType("application/octet-stream;charset=UTF-8");
		IOUtils.write(zip, response.getOutputStream());
	}

	/**
	 * 下载doc文件
	 *
	 * @param response
	 * @param file
	 * @throws Exception
	 */
	public static void downloadFile(HttpServletResponse response, File file) throws Exception {
		response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "utf-8"));
		ServletOutputStream os = response.getOutputStream();
		FileInputStream fis = new FileInputStream(file);
		copyStream(fis, os, false);
		fis.close();
	}

	/*
	 * @param ips  输入流
	 * @param ops  输出流
	 * @param closeStream  是否关闭流
	 * @throws Exception
	 */
	private static void copyStream(InputStream ips, OutputStream ops, boolean closeStream) throws Exception {
		byte[] buf = new byte[1024];
		int len = ips.read(buf);
		while (len != -1) {
			ops.write(buf, 0, len);
			len = ips.read(buf);
		}
	}

	/**
	 * 将文件夹压缩为压缩包并压缩到指定位置
	 *
	 * @param srcSource
	 * @return
	 * @throws Exception
	 */
	public static String createZipFile(String srcSource, String ossPath, String path, String fileName) throws Exception {
		//构建压缩包存放路径
		String filePath = ossPath + path;
		//创建存放压缩包的文件夹
		File folderFile = new File(filePath);
		if (!folderFile.exists()) {
			if (!folderFile.mkdirs()) {
				LogUtil.error("新建压缩包存放文件夹失败！");
				return "";
			}
		}
		//构建压缩文件File
		File zipFile = new File(filePath + "/" + fileName);
		ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(zipFile));
		// 将目标文件打包成zip导出
		File file = new File(srcSource);
		compress(zip, file, "");
		IOUtils.closeQuietly(zip);
		return path + "/" + fileName;
	}

	//压缩文件夹
	private static void compress(ZipOutputStream zip, File file, String dir) throws Exception {
		// 如果当前的是文件夹，则进行进一步处理
		try {
			if (file.isDirectory()) {
				// 得到文件列表信息
				File[] files = file.listFiles();
				// 将文件夹添加到下一级打包目录
				zip.putNextEntry(new ZipEntry(dir + "/"));
				dir = dir.length() == 0 ? "" : dir + "/";
				// 循环将文件夹中的文件打包
				for (int i = 0; i < files.length; i++) {
					compress(zip, files[i], dir + files[i].getName());
				}
			} else {
				// 当前的是文件，打包处理文件输入流
				BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
				ZipEntry entry = new ZipEntry(dir);
				zip.putNextEntry(entry);
				zip.write(FileUtils.readFileToByteArray(file));
				IOUtils.closeQuietly(bis);
			}
		} catch (Exception e) {
			// TODO: handle exception
			zip.flush();
			zip.close();
		}
	}

	/**
	 * 递归删除
	 * 删除某个目录及目录下的所有子目录和文件
	 *
	 * @param file 文件或目录
	 * @return 删除结果
	 */
	public static boolean delFiles(File file) {
		boolean result = false;
		//目录
		if (file.isDirectory()) {
			File[] childrenFiles = file.listFiles();
			for (File childFile : childrenFiles) {
				result = delFiles(childFile);
				if (!result) {
					return result;
				}
			}
		}
		//删除 文件、空目录
		result = file.delete();
		return result;
	}
}
